---
title: "Telemetry Service"
type: entity
created: 2026-04-18
updated: 2026-04-18
sources: ["raw/articles/06-reading-telemetry.md", "raw/notes/memory.md", "raw/articles/03-architecture.md"]
tags: [service, telemetry, event-pipeline, offline-first]
---

# Telemetry Service

The Telemetry Service is the event ingestion backbone of the [[Pickatale]] platform. Every reading action a student takes in the [[Reader App]] — turning a page, tapping a word, starting or ending a session — is captured by this service and routed into the platform's analysis pipelines.

## Service Details

| Attribute | Value |
|---|---|
| Port | 3110 |
| Code location | `/home/ubuntu/telemetry/` |
| Database | `telemetry` on shared MySQL |
| Auth | `X-Internal-Key` header (5-second timeout on outbound POSTs) |
| Retention | 7-day rolling window |

## Event Types

The Telemetry Service ingests four core event types:

| Event | Payload | Notes |
|---|---|---|
| `session_start` | `learner_id`, `book_id`, `timestamp` | Marks the beginning of a reading session |
| `session_end` | `learner_id`, `book_id`, `duration_s`, `pages_read` | Triggers downstream pipeline (see below) |
| `page_turned` | `learner_id`, `book_id`, `page`, `timestamp` | Tracks reading pace and completion |
| `word_tapped` | `learner_id`, `word`, `book_id`, `page`, `timestamp` | Vocabulary gap signal; feeds vocab-taps POST |

Each event carries a `event_id` UUID field used for deduplication — duplicate submissions (e.g., from retry after network failure) are silently dropped on the server.

## Session Summary Pipeline

When a `session_end` event is received and the session record is finalized, the Telemetry Service fires two **parallel** POST requests to the [[Learner Bot]] (port 3120):

```mermaid
sequenceDiagram
    participant R as Reader App
    participant T as Telemetry Service (3110)
    participant LB as Learner Bot (3120)

    R->>T: POST /events/session_end
    T->>T: Finalize session record
    par Parallel dispatch
        T->>LB: POST /ingest/vocab-taps  (word_tapped events for session)
        T->>LB: POST /ingest/session-summary  (FK level, WCPM, pages, duration)
    end
    LB-->>T: 200 OK (each leg, 5s timeout)
```

Both requests carry the `X-Internal-Key` authentication header and have a **5-second timeout**. If either leg fails (timeout or non-2xx), the error is logged and the session record is flagged `sync_failed` for retry on the next heartbeat. The session data is never lost — only the downstream push is retried.

**⚠️ Non-Negotiable (Sig):** Telemetry must never block the Reader App. The `session_end` event is accepted and persisted before the parallel POSTs are fired. The Reader App receives a `200 OK` immediately — it does not wait for Learner Bot acknowledgement.

## Offline-First Architecture

The Reader App operates entirely offline once a book is cached. Telemetry events accumulate locally in **IndexedDB** during offline sessions. A **Background Sync Service Worker** queues these events under the `telemetry-sync` tag and replays them to the Telemetry Service once connectivity is restored.

```mermaid
flowchart LR
    RA[Reader App] -->|Online| T[Telemetry Service 3110]
    RA -->|Offline| IDB[IndexedDB Queue]
    IDB -->|BackgroundSync SW| T
    T --> DB[(telemetry DB)]
```

The `event_id` UUID on each event ensures that replayed batches do not produce duplicate records — the server deduplicates on `event_id` before writing.

## Data Retention

**⚠️ Non-Negotiable (Sig):** Telemetry raw events are retained for **7 days only**. The [[Learner Bot]] nightly cycle aggregates and stores derived signals (vocab gaps, reading patterns, session summaries) in its own per-child memory store before the raw events age out. The LRS (Learning Record Store) holds permanent records for compliance, but that is a separate system.

The Learner Bot also runs a dedicated **telemetry retention cron at 03:00 UTC** that archives and purges raw events older than 7 days.

## Security and Authentication

All inbound events from the Reader App are authenticated via the logged-in student session. Outbound calls from the Telemetry Service to the [[Learner Bot]] use the `X-Internal-Key` header. The key value is stored in environment variables only (`INTERNAL_KEY` in `.env`) — never in source code or documentation.

Telemetry endpoints are internal-only. They are not exposed through the public Caddy reverse proxy.

## Related Pages

- [[entities/Reader App]] — produces all telemetry events
- [[entities/Learner Bot]] — consumes session summaries and vocab-tap batches
- [[concepts/user-flows/Student Flow]] — describes the full reading session lifecycle
- [[concepts/architecture/Data Flow]] — platform-wide data flow including telemetry
- [[concepts/learner-bot/Nightly Cycle]] — how aggregated telemetry drives the nightly AI run
